<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 
<html>
<head>
<title>Resizable component in Java Swing</title>
<link rel="stylesheet" href="/cfg/format.css" type="text/css">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="keywords" content="Java, Swing, resizable component, tutorial, programming, Linux">
<meta name="description" content="This part of the Java Swing tutorial presents a 
resizable component in Java Swing.">
<meta name="language" content="en">
<meta name="author" content="Jan Bodnar">
<meta name="distribution" content="global">

<script type="text/javascript" src="/lib/jquery.js"></script>
<script type="text/javascript" src="/lib/common.js"></script>

</head>

<body>

<div class="container">

<div id="wide_ad" class="ltow">
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* 160x600, August 2011 */
google_ad_slot = "2484182563";
google_ad_width = 160;
google_ad_height = 600;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>

<div class="content">


<a href="/" title="Home">Home</a>&nbsp;
<a href=".." title="Home">Contents</a>


<h1>Resizable components in Java Swing</h1>

<p>
In this part of the Java Swing tutorial, we will create a resizable component. 
</p>

<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* NewSquare */
google_ad_slot = "0364418177";
google_ad_width = 300;
google_ad_height = 250;
//-->
</script> 
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> 
</script>

<h2>Resizable component</h2>

<p>
Resizable components are most often used when creating charts, 
diagrams and similar. The most common resizable component
is a chart in a spreadsheet application. For example, when we 
create a chart in a OpenOffice application. The chart can 
be moved over the grid widget of the application and resized. 
</p>

<p>
In order to create a component that can be freely dragged over 
a panel, we need a panel with absolute positioning enabled.
We must not use a layout manager. 
In our example, we will create a component (a JPanel) that we 
can freely move over a parent window and resize. 
</p>

<p>
In order to distinguish which component has a focus, we draw 8 
small rectangles on the border of our resizable component. 
This way we know, that the component has focus. The rectangles 
serve as a dragging points, where we can draw the
component and start resizing. I have learnt to use resizable 
components from <a href="http://www.jroller.com/santhosh/category/Swing">this</a> blog.
</p>


<pre class="code">
package resizablecomponent;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JFrame;
import javax.swing.JPanel;


/* ResizableComponent.java */

public class ResizableComponent extends JFrame {

  private JPanel panel = new JPanel(null);
  private Resizable resizer;


  public ResizableComponent() {

      add(panel);

      JPanel area = new JPanel(); 
      area.setBackground(Color.white);
      resizer = new Resizable(area);
      resizer.setBounds(50, 50, 200, 150);
      panel.add(resizer);

 
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      setSize(new Dimension(350, 300));
      setTitle("Resizable Component");
      setLocationRelativeTo(null);

      addMouseListener(new MouseAdapter() {
        public void mousePressed(MouseEvent me) {

          requestFocus();
          resizer.repaint();
        }
      });
  }

  public static void main(String[] args) {
      ResizableComponent rc = new ResizableComponent();
      rc.setVisible(true);
  }
}
</pre>

<p>
The <code>ResizableComponent</code> sets up the 
panel and the component. 
</p>


<pre class="explanation">
private JPanel panel = new JPanel(null);
</pre>

<p>
We have already mentioned, that we cannot use any layout manager. We 
must use absolute positioning for resizable component.
By providing null to the constructor, we create a panel with 
absolute positioning.
</p>

<pre class="explanation">
addMouseListener(new MouseAdapter() {
  public void mousePressed(MouseEvent me) {

    requestFocus();
    resizer.repaint();
  }
});
</pre>

<p>
If we press on the parent panel, e.g outside the resizable component, 
we grab focus and repaint the component. The rectangles over 
he border will disappear. 
</p>

<pre class="code">
package resizablecomponent;

import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;

import javax.swing.SwingConstants;
import javax.swing.border.Border;

// ResizableBorder.java 

public class ResizableBorder implements Border {
  private int dist = 8;

  int locations[] = 
  {
    SwingConstants.NORTH, SwingConstants.SOUTH, SwingConstants.WEST,
    SwingConstants.EAST, SwingConstants.NORTH_WEST,
    SwingConstants.NORTH_EAST, SwingConstants.SOUTH_WEST,
    SwingConstants.SOUTH_EAST
  };

  int cursors[] =
  { 
    Cursor.N_RESIZE_CURSOR, Cursor.S_RESIZE_CURSOR, Cursor.W_RESIZE_CURSOR,
    Cursor.E_RESIZE_CURSOR, Cursor.NW_RESIZE_CURSOR, Cursor.NE_RESIZE_CURSOR,
    Cursor.SW_RESIZE_CURSOR, Cursor.SE_RESIZE_CURSOR
  };

  public ResizableBorder(int dist) {
    this.dist = dist;
  }

  public Insets getBorderInsets(Component component) {
      return new Insets(dist, dist, dist, dist);
  }

  public boolean isBorderOpaque() {
      return false;
  }

  public void paintBorder(Component component, Graphics g, int x, int y,
                          int w, int h) {
      g.setColor(Color.black);
      g.drawRect(x + dist / 2, y + dist / 2, w - dist, h - dist);
 
      if (component.hasFocus()) {
 

        for (int i = 0; i < locations.length; i++) {
          Rectangle rect = getRectangle(x, y, w, h, locations[i]);
          g.setColor(Color.WHITE);
          g.fillRect(rect.x, rect.y, rect.width - 1, rect.height - 1);
          g.setColor(Color.BLACK);
          g.drawRect(rect.x, rect.y, rect.width - 1, rect.height - 1);
        }
      }
  }

  private Rectangle getRectangle(int x, int y, int w, int h, int location) {
      switch (location) {
      case SwingConstants.NORTH:
          return new Rectangle(x + w / 2 - dist / 2, y, dist, dist);
      case SwingConstants.SOUTH:
          return new Rectangle(x + w / 2 - dist / 2, y + h - dist, dist,
                               dist);
      case SwingConstants.WEST:
          return new Rectangle(x, y + h / 2 - dist / 2, dist, dist);
      case SwingConstants.EAST:
          return new Rectangle(x + w - dist, y + h / 2 - dist / 2, dist,
                               dist);
      case SwingConstants.NORTH_WEST:
          return new Rectangle(x, y, dist, dist);
      case SwingConstants.NORTH_EAST:
          return new Rectangle(x + w - dist, y, dist, dist);
      case SwingConstants.SOUTH_WEST:
          return new Rectangle(x, y + h - dist, dist, dist);
      case SwingConstants.SOUTH_EAST:
          return new Rectangle(x + w - dist, y + h - dist, dist, dist);
      }
      return null;
  }

  public int getCursor(MouseEvent me) {
      Component c = me.getComponent();
      int w = c.getWidth();
      int h = c.getHeight();

      for (int i = 0; i < locations.length; i++) {
          Rectangle rect = getRectangle(0, 0, w, h, locations[i]);
          if (rect.contains(me.getPoint()))
              return cursors[i];
      }

      return Cursor.MOVE_CURSOR;
  }
}
</pre>

<p>
The <code>ResizableBorder</code> is responsible for drawing 
the border of the component and determining 
the type of the cursor to use. 
</p>

<pre class="explanation">
int locations[] = 
{
  SwingConstants.NORTH, SwingConstants.SOUTH, SwingConstants.WEST,
  SwingConstants.EAST, SwingConstants.NORTH_WEST,
  SwingConstants.NORTH_EAST, SwingConstants.SOUTH_WEST,
  SwingConstants.SOUTH_EAST
};
</pre>

<p>
These are locations, where we will draw rectangles. These locations are 
grabbing points, where we can grab the
component and resize it. 
</p>

<pre class="explanation">
g.setColor(Color.black);
g.drawRect(x + dist / 2, y + dist / 2, w - dist, h - dist);
</pre>

<p>
In the <code>paintBorder()</code> method, we draw the border 
of the resizable component. 
The upper code draws the outer border of the component. 
</p>

<pre class="explanation">
if (component.hasFocus()) {

  for (int i = 0; i < locations.length; i++) {
    Rectangle rect = getRectangle(x, y, w, h, locations[i]);
    g.setColor(Color.WHITE);
    g.fillRect(rect.x, rect.y, rect.width - 1, rect.height - 1);
    g.setColor(Color.BLACK);
    g.drawRect(rect.x, rect.y, rect.width - 1, rect.height - 1);
  }
}
</pre>

<p>
The eight rectangles are drawn only in case that the resizable component 
has currently focus.
</p>

<p>
Finally, the <code>getRectangle()</code> method gets the coordinates of 
the rectangles and the <code>getCursor()</code> methods gets the
cursor type for the grab point in question.
</p>


<pre class="code">
package resizablecomponent;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;

import javax.swing.JComponent;
import javax.swing.event.MouseInputAdapter;
import javax.swing.event.MouseInputListener;

// Resizable.java 

public class Resizable extends JComponent {

  public Resizable(Component comp) {
    this(comp, new ResizableBorder(8));
  }

  public Resizable(Component comp, ResizableBorder border) {
    setLayout(new BorderLayout());
    add(comp);
    addMouseListener(resizeListener);
    addMouseMotionListener(resizeListener);
    setBorder(border);
  }

  private void resize() {
      if (getParent() != null) {
        ((JComponent)getParent()).revalidate();
      }
  }

  MouseInputListener resizeListener = new MouseInputAdapter() {
    public void mouseMoved(MouseEvent me) {
      if (hasFocus()) {
          ResizableBorder border = (ResizableBorder)getBorder();
          setCursor(Cursor.getPredefinedCursor(border.getCursor(me)));
      }
    }

    public void mouseExited(MouseEvent mouseEvent) {
       setCursor(Cursor.getDefaultCursor());
    }

    private int cursor;
    private Point startPos = null;

    public void mousePressed(MouseEvent me) {
      ResizableBorder border = (ResizableBorder)getBorder();
      cursor = border.getCursor(me);
      startPos = me.getPoint();
      requestFocus();
      repaint();
    }

    public void mouseDragged(MouseEvent me) {

      if (startPos != null) {

        int x = getX();
        int y = getY();
        int w = getWidth();
        int h = getHeight();

        int dx = me.getX() - startPos.x;
        int dy = me.getY() - startPos.y;
 
        switch (cursor) {
          case Cursor.N_RESIZE_CURSOR:
            if (!(h - dy < 50)) {
              setBounds(x, y + dy, w, h - dy);
              resize();
            }
            break;

          case Cursor.S_RESIZE_CURSOR:
            if (!(h + dy < 50)) {
              setBounds(x, y, w, h + dy);
              startPos = me.getPoint();
              resize();
            }
            break;

          case Cursor.W_RESIZE_CURSOR:
            if (!(w - dx < 50)) {
              setBounds(x + dx, y, w - dx, h);
              resize();
            }
            break;

          case Cursor.E_RESIZE_CURSOR:
            if (!(w + dx < 50)) {
              setBounds(x, y, w + dx, h);
              startPos = me.getPoint();
              resize();
            }
            break;

          case Cursor.NW_RESIZE_CURSOR:
            if (!(w - dx < 50) &amp;&amp; !(h - dy < 50)) {
              setBounds(x + dx, y + dy, w - dx, h - dy);
              resize();
            }
            break;

          case Cursor.NE_RESIZE_CURSOR:
            if (!(w + dx < 50) &amp;&amp; !(h - dy < 50)) {
              setBounds(x, y + dy, w + dx, h - dy);
              startPos = new Point(me.getX(), startPos.y);
              resize();
            }
            break;

          case Cursor.SW_RESIZE_CURSOR:
            if (!(w - dx < 50) &amp;&amp; !(h + dy < 50)) {
              setBounds(x + dx, y, w - dx, h + dy);
              startPos = new Point(startPos.x, me.getY());
              resize();
            }
            break;

          case Cursor.SE_RESIZE_CURSOR:
            if (!(w + dx < 50) &amp;&amp; !(h + dy < 50)) {
              setBounds(x, y, w + dx, h + dy);
              startPos = me.getPoint();
              resize();
            }
          break;

          case Cursor.MOVE_CURSOR:
            Rectangle bounds = getBounds();
            bounds.translate(dx, dy);
            setBounds(bounds);
            resize();
          }


          setCursor(Cursor.getPredefinedCursor(cursor));
        }
     }

   public void mouseReleased(MouseEvent mouseEvent) {
     startPos = null;
    }
  };
}
</pre>


<p>
The <code>Resizable</code> class represents the component, that is 
being resized and moved on the window.
</p>

<pre class="explanation">
private void resize() {
    if (getParent() != null) {
      ((JComponent)getParent()).revalidate();
    }
}
</pre>

<p>
The <code>resize()</code> method is called, after we have resized 
the component. The <code>revalidate()</code> method
will cause the component to be redrawn. 
</p>

<pre class="explanation">
MouseInputListener resizeListener = new MouseInputAdapter() {
  public void mouseMoved(MouseEvent me) {
    if (hasFocus()) {
        ResizableBorder border = (ResizableBorder)getBorder();
        setCursor(Cursor.getPredefinedCursor(border.getCursor(me)));
    }
}
</pre>

<p>
We change the cursor type, when we hover the cursor over the grip points. 
The cursor type changes only if the component has
focus.
</p>

<pre class="explanation">
public void mousePressed(MouseEvent me) {
  ResizableBorder border = (ResizableBorder)getBorder();
    cursor = border.getCursor(me);
    startPos = me.getPoint();
    requestFocus();
    repaint();
}
</pre>

<p>
If we click on the resizable component, we change the cursor, get the starting 
point of dragging, give focus to the
component and redraw it. 
</p>

<pre class="explanation">
int x = getX();
int y = getY();
int w = getWidth();
int h = getHeight();

int dx = me.getX() - startPos.x;
int dy = me.getY() - startPos.y;
</pre>

<p>
In the <code>mouseDragged()</code> method, we determine the x, y 
coordinates of the cursor, width and height of the
component. We calculate the distances, that we make during the mouse drag event.  
</p>

<pre class="explanation">
case Cursor.N_RESIZE_CURSOR:
  if (!(h - dy &lt; 50)) {
   setBounds(x, y + dy, w, h - dy);
   resize();
  }
  break;
</pre>

<p>
For all resizing we ensure, that the component is not smaller than 50 px. 
Otherwise, we could make it so small, that we
would eventually hide the component. The <code>setBounds()</code> 
method relocates and resizes the component. 
</p>

<img src="/img/gui/javaswing/resizablecomponent.png" alt="Resizable component">
<div class="figure">Figure: Resizable component</div>


<p>
In this part of the Java Swing tutorial, we have created a resizable component. 
</p>


<div class="center"> 
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* horizontal */
google_ad_slot = "1734478269";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script> 
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> 
</script> 
</div> 
<br>

<div class="botNav, center">
<span class="botNavItem"><a href="/">Home</a></span> ‡ <span class="botNavItem"><a href="..">Contents</a></span> ‡ 
<span class="botNavItem"><a href="#">Top of Page</a></span>
</div>


<div class="footer">
<div class="signature">
<a href="/">ZetCode</a> last modified February 20, 2008  <span class="copyright">&copy; 2007 - 2013 Jan Bodnar</span>
</div>
</div>

</div> <!-- content -->

</div> <!-- container -->

</body>
</html>
